package com.thread;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/*
线程池参数说明

1. 核心线程和最大线程数量

corePoolSize用于设置核心（Core）线程池数量，参数maximumPoolSize用于设置最大线程数量
线程池接收到新任务，当前工作线程数少于corePoolSize, 即使有空闲的工作线程，也会创建新的线程来处理该请求，直到线程数达到corePoolSize
当前工作线程数多于corePoolSize数量，但小于maximumPoolSize数量，那么仅当任务排队队列已满时才会创建新线程
maximumPoolSize被设置为无界值（如Integer.MAX_VALUE）时，线程池可以接收任意数量的并发任务

2. BlockingQueue
BlockingQueue（阻塞队列）的实例用于暂时接收到的异步任务，如果线程池的核心线程都在忙，那么所接收到的目标任务缓存在阻塞队列中

3. keepAliveTime

空闲线程存活时间
用于设置池内线程最大Idle（空闲）时长（或者说保活时长）
超过这个时间，默认情况下Idle、非Core线程会被回收
注意：若调用了allowCoreThreadTimeOut(boolean)方法，并且传入了参数true，则keepAliveTime参数所设置的Idle超时策略也将被应用于核心线程

        任务阻塞队列
        特点：在一个线程从一个空的阻塞队列中获取元素时线程会被阻塞，直到阻塞队列中有了元素；当队列中有元素后，被阻塞的线程会自动被唤醒
        常见的几种阻塞队列的实现：
        ArrayBlockingQueue：是一个数组实现的有界阻塞队列（有界队列），队列中的元素按FIFO排序，ArrayBlockingQueue在创建时必须设置大小
        LinkedBlockingQueue：是一个基于链表实现的阻塞队列，按FIFO排序任务，可以设置容量（有界队列），不设置容量则默认使用Integer.Max_VALUE作为容量（无界队列）
        PriorityBlockingQueue：是具有优先级的无界队列
*/

//自定义线程工厂
class SimpleThreadFactory implements ThreadFactory {
    static AtomicInteger threadNo = new AtomicInteger(1);

    public Thread newThread(Runnable task) {
        String threadName = "simpleThread-" + threadNo;
        System.out.println("创建一条线程，名字是：" + threadName);
        threadNo.incrementAndGet();
        Thread thread = new Thread(task, threadName);
        thread.setDaemon(true);
        return thread;
    }
}

//自定义拒绝策略
class CustomerIgnorePolicy implements RejectedExecutionHandler{
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println(Thread.currentThread().getName()+"-rejected;  taskCount-"+executor.getTaskCount());

    }

}

/**
 * @description: TODO edit class Description
 * @create date: 2023/2/21 23:09
 * 线程池缺点：
 * newFixedThreadPool和newSingleThreadExecutor： 阻塞队列无界，会堆积大量任务导致OOM(内存耗尽)
 * newCachedThreadPool和newScheduledThreadPool： 线程数量无上界，会导致创建大量的线程，从而导致OOM
 * 建议直接使用线程池ThreadPoolExecutor的构造器
 */
public class ThreadPoolSingle {
    public static void main(String[] args) {
        int corePoolSize=2;//核心线程数
        int maximumPoolSize=4;//最大线程数
        long keepAlive=10;//空闲时间
        TimeUnit unit=TimeUnit.SECONDS;//时间单位
        BlockingQueue<Runnable> workQueue=new LinkedBlockingQueue<>(2);//阻塞队列
        ThreadFactory factory=new SimpleThreadFactory();//自定义线程工厂
//        RejectedExecutionHandler policy=new CustomerIgnorePolicy();//自定义拒绝策略
        ThreadPoolExecutor pool=new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAlive,unit,workQueue,factory,new ThreadPoolExecutor.AbortPolicy());
    }


    /**
     * newSingleThreadExecutor 创建“单线程化线程池”特点：
     *  * 单线程化的线程池中的任务是按照提交的次序顺序执行的
     *  * 只有一个线程的线程池
     *  * 池中的唯一线程的存活时间是无限的
     *  * 当池中的唯一线程正繁忙时，新提交的任务实例会进入内部的阻塞队列中，并且其阻塞队列是无界的
     *  * 适用场景：任务按照提交次序，一个任务一个任务地逐个执行的场景
     *  缺点：问题和固定数量线程池一样，阻塞队列无界
     */
    public void singleThreadExecutor(){
        ExecutorService pool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            pool.execute(() -> {
                System.out.println(finalI + "name:" + Thread.currentThread().getName());
            });
            /*pool.submit(() -> {
                System.out.println("name2:" + Thread.currentThread().getName());
            })*/
        }
        pool.shutdown();
    }


    /**
     * 特点：
     *
     * 如果线程数没有达到“固定数量”，每次提交一个任务线程池内就创建一个新线程，直到线程达到线程池固定的数量
     * 线程池的大小一旦达到“固定数量”就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程
     * 在接收异步任务的执行目标实例时，如果池中的所有线程均在繁忙状态，新任务会进入阻塞队列中（无界的阻塞队列）
     * 适用场景：
     * 需要任务长期执行的场景
     * CPU密集型任务
     * 缺点：
     * 内部使用无界队列来存放排队任务，当大量任务超过线程池最大容量需要处理时，队列无限增大，使服务器资源迅速耗尽
     * 阻塞队列无界，队列很大，很有可能导致JVM出现OOM（Out Of Memory）异常，即内存资源耗尽
     */
    public void fixedThreadPool() {
        ExecutorService pool = Executors.newFixedThreadPool(3);//创建含有3个线程的线程池
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            pool.execute(() -> {
                System.out.println(finalI + "name:" + Thread.currentThread().getName());
            });
            pool.submit(() -> {
                System.out.println(finalI + "name:" + Thread.currentThread().getName());
            });
        }
        pool.shutdown();
    }

    /**
     * 特点：
     * 在接收新的异步任务target执行目标实例时，如果池内所有线程繁忙，此线程池就会添加新线程来处理任务
     * 线程池不会对线程池大小进行限制，线程池大小完全依赖于操作系统（或者说JVM）能够创建的最大线程大小
     * 如果部分线程空闲，也就是存量线程的数量超过了处理任务数量，就会回收空闲（60秒不执行任务）线程
     * 适用场景：
     * 需要快速处理突发性强、耗时较短的任务场景，如Netty的NIO处理场景、REST API接口的瞬时削峰场景
     * 缺点：
     * 线程池没有最大线程数量限制，如果大量的异步任务执行目标实例同时提交，可能会因创建线程过多而导致资源耗尽
     */
    public void cachedThreadPool() {
        ExecutorService pool = Executors.newCachedThreadPool();//创建含有3个线程的线程池
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            pool.execute(() -> {
                System.out.println(finalI + "name:" + Thread.currentThread().getName());
            });
            pool.submit(() -> {
                System.out.println(finalI + "name:" + Thread.currentThread().getName());
            });
        }
        pool.shutdown();
    }

    public void scheduledThreadPool() {
        ScheduledExecutorService  pool = Executors.newScheduledThreadPool(3);//创建含有3个线程的线程池
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            pool.scheduleAtFixedRate(() -> {
                System.out.println(finalI + "name:" + Thread.currentThread().getName());
            },0,500,TimeUnit.MILLISECONDS);
            //参数1： task任务
            //参数2： 首次执行任务的延迟时间
            //参数3： 周期性执行的时间
            //参数4： 时间单位
        }
        pool.shutdown();
    }
}